using System;
using System.Collections.Generic;
using System.Threading;

namespace SBPweb.Logging
{
    public static partial class LogManager
    {
        private static Dictionary<int, Dictionary<String, int>> mImplicitCategories = new Dictionary<int, Dictionary<string, int>>();  // Thread - implicit category pairs

        private static volatile ICollection<String> mGlobalImplicitCategories = new List<String>();

        /// <summary>
        /// Gets or sets the collection of thread independent implicit categories.
        /// </summary>
        /// <remarks>
        /// You may force a log entry to be written more than once to a given category by adding it explicily to the category
        /// collection of the log entry and adding it to the global implicit category collection at the same time. Be careful!
        /// </remarks>
        /// <example>
        /// The following example shows how to add implicit categories to every log entries.
        /// <code>
        /// LogManager.GlobalImplicitCategories=new String [] {"LocalTraceListeners", "NetworkTraceListeners"};
        /// 
        /// // Any subsequent write will be written to "LocalTraceListeners" and "NetworkTraceListeners" categories.
        /// LogManager.Write(logentry);
        /// </code>
        /// </example>
        public static ICollection<String> GlobalImplicitCategories
        {
            get
            {
                return mGlobalImplicitCategories;
            }
            set
            {
                mGlobalImplicitCategories = value == null ? new List<String>() : value;
            }
        }

        /// <summary>
        /// Add a new set of implicit categories to the thread specific set.
        /// </summary>
        /// <param name="ImplicitCategories">Set of implicit categories to add.</param>
        /// <remarks>
        /// This method is thread safe.
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <example>
        /// The following example demonstrates the adequat way of implicit category definition.
        /// <code>
        /// using (new (new string [] {"ImplicitCategory_1", "ImplicitCategory_2"}))
        /// {
        ///     using (new KeyWord("ImplicitCategory_3")
        ///     {
        ///         LogManager.WriteCritical("Message");
        ///     }
        /// }
        /// </code>
        /// </example>
        internal static void AddImplicitCategories(ICollection<String> ImplicitCategories)
        {
            lock (mImplicitCategories)
            {
                if (!mInitialized)
                    return;

                Dictionary<String, int> implicitcategories;

                if (!mImplicitCategories.TryGetValue(Thread.CurrentThread.ManagedThreadId, out implicitcategories))
                    implicitcategories = mImplicitCategories[Thread.CurrentThread.ManagedThreadId] = new Dictionary<string, int>();

                int refcount = 0;

                foreach (String implicitcategory in ImplicitCategories)
                {
                    implicitcategories[implicitcategory] = implicitcategories.TryGetValue(implicitcategory, out refcount) ? refcount + 1 : 1;
                }
            }
        }

        /// <summary>
        /// Removes a new set of implicit categories from the thread specific set.
        /// </summary>
        /// <param name="ImplicitCategories">Set of implicit categories to remove.</param>
        /// <remarks>
        /// This method is thread safe.
        /// This method supports the EntLib infrastructure.
        /// </remarks>
        /// <example>
        /// The following example demonstrates the adequat way of implicit category definition.
        /// <code>
        /// using (new (new string [] {"ImplicitCategory_1", "ImplicitCategory_2"}))
        /// {
        ///     using (new KeyWord("ImplicitCategory_3")
        ///     {
        ///         LogManager.WriteCritical("Message");
        ///     }
        /// }
        /// </code>
        /// </example>
        internal static void RemoveImplicitCategories(ICollection<String> ImplicitCategories)
        {
            lock (mImplicitCategories)
            {
                if (!mInitialized)
                    return;

                Dictionary<String, int> implicitcategories;

                try
                {
                    // It is enough to lock mKeywords just til we retrieve the inner and thread specific collection.
                    implicitcategories = mImplicitCategories[Thread.CurrentThread.ManagedThreadId];
                }
                catch (KeyNotFoundException)
                {
                    // May not really happen
                    return;
                }

                List<String> removeable = new List<string>(implicitcategories.Count);
                int refcount;

                foreach (String implicitcategory in ImplicitCategories)
                {
                    try
                    {
                        if ((refcount = implicitcategories[implicitcategory]) == 1)
                            removeable.Add(implicitcategory);
                        else
                            implicitcategories[implicitcategory] = refcount - 1;
                    }
                    catch (KeyNotFoundException)
                    {
                        // May not really happen
                    }
                }

                foreach (String implicitcategory in removeable)
                    implicitcategories.Remove(implicitcategory);

                removeable.Clear();

                if (implicitcategories.Count == 0)
                {
                    lock (mImplicitCategories)
                    {
                        mImplicitCategories.Remove(Thread.CurrentThread.ManagedThreadId);
                    }
                }
            }
        }
    }
}